<?
declare (strict_types = 1);
namespace core;

use PDO;
use core\Pool;

class Db
{
    protected static $pool = null;

    protected $pdo = null;

    public $apiData = null;

    public $query=[
        'debug'=>false,
        'table'=>'',
        'action'=>'',
        'fields'=>'*',
        'where'=>'',
        'orderBy'=>'',
        'groupBy'=>'',
        'limit'=>'',
        'execute'=>[],
        'sql'=>''
    ];

    public static function init($connect='default')
    {
        if (self::$pool == null) {
            $c = config('db');
            foreach ($c as $k => $v) {
                self::$pool[$k] = new Pool(function () use ($v) {

                    try {
                        $dsn = "mysql:host={$v['host']};dbname={$v['database']};port={$v['port']};charset={$v['charset']}";
                        $opt = [
                            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                            PDO::ATTR_STRINGIFY_FETCHES => false,
                            PDO::ATTR_EMULATE_PREPARES => false, // 这2个是跟数字相关的设置
                        ];
                        $db =  new \PDO($dsn, $v['username'], $v['password'], $opt);
                        return $db;
                    } catch (\Throwable $e) {
                        loger($e);
                        die();
                    }

                }, 10);
            }
            loger('init');
            //heart;
            // Timer::tick(1000*5,function(){
            //     $p=self::$pool[$connect??self::$connect]->get();
            //     self::$pool[$connect??self::$connect]->put($p);
            // });
        }

        return self::$pool[$connect];
    }

    public static function table($table='table')
    {
        $curr = new self();
        $curr->query['table']=$table;
        return $curr;
    }

    public static function raw($sql='')
    {
        $curr=new self();
        $curr->query['sql']=$sql;
        return $curr;
    }

    // public function __construct($data=null){
    //     $this->data = $data;
    // }
    
    public function __destruct() {
        if($this->query['debug']){
            loger($this);
        }
        Db::init()->put($this->pdo);
        $this->pdo = null;

        loger('DB __destruct');
    }

    public function __checkSql($action='')
    {
        $must = [];
        switch ($action) {
            case 'insert':
                $must = ['table','execute'];
                break;
            case 'update':
                $must=['table','execute','where'];
                break;
            case 'delete':
            case 'select':
                $must=['table','where'];
                break;
        }
        foreach ($must as $k) {
            if(empty($this->query[$k])){
                throw new \Exception("$k 是必须的", 1);
            }
        }
        return $this;
    }

    public function insert($data=[])
    {
        $data = empty($data)?$this->apiData:$data;
        $key = '(`'.implode("`,`", array_keys($data)).'`)';
        $val = '('.implode(",", array_fill(0, count($data), '?')).')' ;
        $this->query['sql'] = "INSERT INTO `{$this->query['table']}` $key VALUES $val";
        $this->query['execute'] = array_values($data);
        $this->execute('insert');
        return $this->pdo->lastInsertId();
    }

    public function delete()
    {
        $this->query['sql'] = "DELETE FROM {$this->query['table']}{$this->query['where']}";
        return $this->execute('delete')->rowCount();
    }

    public function update($data=[])
    {
        $data = empty($data)?$this->apiData:$data;
        $sets=[];
        foreach ($data as $k => $v) {
            $sets[]="`$k`=?";
            $this->query['execute'][]=$v;
        }
        $this->query['sql'] = "UPDATE `{$this->query['table']}` SET ".implode(',',$sets).$this->query['where'];
        return $this->execute('update')->rowCount();
    }

    public function select(bool $first=false)
    {
        $this->query['sql'] = "SELECT {$this->query['fields']} FROM {$this->query['table']}{$this->query['where']}{$this->query['groupBy']}{$this->query['orderBy']}{$this->query['limit']}";
        $h= $this->execute('select');

        return $first ? $h->fetch(PDO::FETCH_OBJ) : $h->fetchAll(PDO::FETCH_OBJ);
    }

    public function list()
    {
        $this->pdo = Db::init()->get();
        $limit = empty($this->query['limit'])?' LIMIT 0 , 10':$this->query['limit'];
        $tSql = "SELECT COUNT(*) AS total FROM {$this->query['table']}{$this->query['where']}{$this->query['groupBy']}{$this->query['orderBy']}";
        $t= $this->pdo->prepare($tSql);
        $t->execute();
        $result['total']=$t->fetch(PDO::FETCH_OBJ)->total;

        $lSql = "SELECT {$this->query['fields']} FROM {$this->query['table']}{$this->query['where']}{$this->query['groupBy']}{$this->query['orderBy']}$limit";
        $l= $this->pdo->prepare($lSql);
        $l->execute();
        $result['list']=$l->fetchAll(PDO::FETCH_ASSOC);
        // Db::init()->put($pdo);
        return $result;
    }

    public function __call($method,$o)
    {

        switch (strtolower($method)) {
            case 'd':
            case 'debug':
                $this->query['debug']=true;
                break;
            case 'whereraw':
                $this->query['where'] .= !empty($this->query['where'])?' AND '.$o[0]:' WHERE '.$o[0];
                break;
            case 'where':
                $this->query['where'] .= !empty($this->query['where'])?' AND '.$this->_parseWhere($o):' WHERE '.$this->_parseWhere($o);
                break;
            case 'orwhere':
                $this->query['where'] = !empty($this->query['where'])?' WHERE ('.substr($this->query['where'], 7).') OR ('.$this->_parseWhere($o).')':' WHERE '.$this->_parseWhere($o);
                break;
            case 'limit':
                $page = (int) $o[0] ?? 0;
                $limit = (int) $o[1] ?? 10;
                $start = ($page > 1 ? $page - 1 : 0)*$limit;
                $this->query['limit'] = " LIMIT $start , $limit";
                break;
            case 'fields':
                $this->query['fields']=$o[0];
                break;
            case 'orderby':
                if (($key=$o[0]??null) && ($sort=$o[1]??null)) {
                    if ($this->query['orderBy']) {
                        $this->query['orderBy'].= " , `$key` $sort";
                    } else {
                        $this->query['orderBy']= " ORDER BY `$key` $sort";
                    }
                }
                break;
            case 'groupby':
                if ($col=$o[0]??null) {
                    if ($this->query['groupBy']) {
                        $this->query['groupBy']= " , `$col` ";
                    } else {
                        $this->query['groupBy']= " GROUP BY `$col`";
                    }
                }
                break;
            case 'parse':
                $data = $o[0]??[];
                if (isset($data['page'])||isset($data['limit'])) {
                    $this->limit($data['page']??0,$data['limit']??10);
                    unset($data['page'],$data['limit']);
                }
                if (isset($data['orderBy']['key']) && isset($data['orderBy']['sort'])) {
                    $this->orderBy($data['orderBy']['key'],$data['orderBy']['sort']);
                    unset($data['orderBy']);
                }
                foreach ($data as $k => $v) {
                    $this->where($k,$v);
                }
                break;
            case 'count':
                $this->query['sql'] = "SELECT COUNT(*) AS total FROM {$this->query['table']}{$this->query['where']}";
                $this->query['action']='count';
                break;
            default:
                loger([$method,$o]);
                break;
        }
        return $this;
    }


    public function execute($method='')
    {
        $this->__checkSql($method);
        $this->pdo = Db::init()->get();
        $h=$this->pdo->prepare($this->query['sql']);
        $h->execute($this->query['execute']);
        return $h;
    }

    public function _parseWhere($v)
    {
        if (!isset($v[2])) {
            array_splice($v, 1, 0, "=");
        }
        [$a,$b,$c] = $v;
        if (is_null($c)) {
            $s = $b=='='?"`$a` IS NULL":"`$a` IS NOT NULL";
        }else if (is_numeric($c)) {
            $s = "`$a`$b$c";
        }else if (empty($c)||is_string($c)) {
            $s = "`$a`$b'$c'";
        }
        return $s;
    }
}
